"""
知识图谱模块
用于管理和查询学科知识图谱
"""
from typing import Dict, List, Any, Optional
from app.models.models import SessionLocal, KnowledgePoint, KnowledgePointRelation, LearningResource
from sqlalchemy.orm import Session
import json

class KnowledgeGraph:
    """知识图谱管理类"""
    
    def __init__(self):
        """初始化知识图谱"""
        pass
    
    def _get_db_session(self) -> Session:
        """获取数据库会话"""
        return SessionLocal()
    
    def get_subject_knowledge_graph(self, subject: str) -> Dict[str, Any]:
        """
        获取学科知识图谱
        
        Args:
            subject: 学科名称
            
        Returns:
            知识图谱数据
        """
        db = self._get_db_session()
        try:
            # 获取该学科的所有知识点
            points = db.query(KnowledgePoint).filter(KnowledgePoint.subject == subject).all()
            
            # 获取知识点间的关系
            point_ids = [p.id for p in points]
            if point_ids:
                relations = db.query(KnowledgePointRelation).filter(
                    KnowledgePointRelation.from_point_id.in_(point_ids)
                ).all()
            else:
                relations = []
            
            # 获取学习资源
            if point_ids:
                resources = db.query(LearningResource).filter(
                    LearningResource.point_id.in_(point_ids)
                ).all()
            else:
                resources = []
            
            # 构建知识图谱结构
            graph = {
                "subject": subject,
                "nodes": [
                    {
                        "id": point.id,
                        "subject": point.subject,
                        "topic": point.topic,
                        "description": point.description,
                        "difficulty": point.difficulty,
                        "estimated_time": point.estimated_time,
                        "prerequisites": [],  # 将在后续处理中填充
                        "dependents": []      # 将在后续处理中填充
                    }
                    for point in points
                ],
                "edges": [
                    {
                        "id": relation.id,
                        "from": relation.from_point_id,
                        "to": relation.to_point_id,
                        "type": relation.relation_type,
                        "weight": float(relation.weight) if relation.weight else 1.0
                    }
                    for relation in relations
                ],
                "resources": [
                    {
                        "id": resource.id,
                        "point_id": resource.point_id,
                        "type": resource.resource_type,
                        "title": resource.title,
                        "url": resource.url,
                        "description": resource.description,
                        "estimated_time": resource.estimated_time
                    }
                    for resource in resources
                ]
            }
            
            # 建立节点ID到节点的映射
            node_map = {node["id"]: node for node in graph["nodes"]}
            
            # 填充节点的先修和依赖关系
            for edge in graph["edges"]:
                from_id = edge["from"]
                to_id = edge["to"]
                edge_type = edge["type"]
                
                if from_id in node_map:
                    if edge_type == "prerequisite":
                        node_map[from_id]["dependents"].append(to_id)
                    # 可以添加其他类型的关系处理
                
                if to_id in node_map:
                    if edge_type == "prerequisite":
                        node_map[to_id]["prerequisites"].append(from_id)
                    # 可以添加其他类型的关系处理
            
            return graph
        finally:
            db.close()
    
    def find_learning_path(self, subject: str, start_point_id: Optional[int] = None, 
                          end_point_id: Optional[int] = None, max_depth: int = 10) -> List[Dict]:
        """
        在知识图谱中查找学习路径
        
        Args:
            subject: 学科名称
            start_point_id: 起始知识点ID
            end_point_id: 结束知识点ID
            max_depth: 最大搜索深度
            
        Returns:
            学习路径
        """
        graph = self.get_subject_knowledge_graph(subject)
        
        # 使用图算法查找路径
        if start_point_id and end_point_id:
            path = self._find_path_dijkstra(graph, start_point_id, end_point_id)
        elif start_point_id:
            path = self._find_path_from_start(graph, start_point_id, max_depth)
        else:
            path = self._find_optimal_starting_path(graph, max_depth)
        
        return path
    
    def _find_path_dijkstra(self, graph: Dict, start_id: int, end_id: int) -> List[Dict]:
        """
        使用Dijkstra算法查找两点间的最短路径
        
        Args:
            graph: 知识图谱
            start_id: 起始节点ID
            end_id: 结束节点ID
            
        Returns:
            路径节点列表
        """
        # 构建邻接表
        adjacency = {}
        for edge in graph["edges"]:
            if edge["type"] == "prerequisite":  # 只考虑先修关系
                if edge["from"] not in adjacency:
                    adjacency[edge["from"]] = []
                adjacency[edge["from"]].append((edge["to"], edge["weight"]))
        
        # Dijkstra算法实现
        import heapq
        distances = {node["id"]: float('inf') for node in graph["nodes"]}
        previous = {node["id"]: None for node in graph["nodes"]}
        distances[start_id] = 0
        
        pq = [(0, start_id)]
        visited = set()
        
        while pq:
            current_distance, current = heapq.heappop(pq)
            
            if current in visited:
                continue
                
            visited.add(current)
            
            if current == end_id:
                break
                
            for neighbor, weight in adjacency.get(current, []):
                distance = current_distance + weight
                
                if distance < distances[neighbor]:
                    distances[neighbor] = distance
                    previous[neighbor] = current
                    heapq.heappush(pq, (distance, neighbor))
        
        # 重构路径
        path = []
        current = end_id
        while current is not None:
            path.append(current)
            current = previous[current]
        
        path.reverse()
        
        # 如果路径没有从起点开始，说明没有找到有效路径
        if path[0] != start_id:
            return []
        
        # 获取路径节点的详细信息
        node_map = {node["id"]: node for node in graph["nodes"]}
        path_nodes = [node_map[node_id] for node_id in path if node_id in node_map]
        
        return path_nodes
    
    def _find_path_from_start(self, graph: Dict, start_id: int, max_depth: int) -> List[Dict]:
        """
        从起始节点开始查找学习路径
        
        Args:
            graph: 知识图谱
            start_id: 起始节点ID
            max_depth: 最大深度
            
        Returns:
            路径节点列表
        """
        # 构建邻接表（仅先修关系）
        adjacency = {}
        for edge in graph["edges"]:
            if edge["type"] == "prerequisite":
                if edge["from"] not in adjacency:
                    adjacency[edge["from"]] = []
                adjacency[edge["from"]].append(edge["to"])
        
        # 使用广度优先搜索查找路径
        from collections import deque
        queue = deque([(start_id, 0)])  # (节点ID, 深度)
        visited = {start_id}
        path = [start_id]
        
        while queue and len(path) < max_depth:
            current, depth = queue.popleft()
            
            # 获取当前节点的后续节点
            next_nodes = adjacency.get(current, [])
            
            for next_node in next_nodes:
                if next_node not in visited:
                    visited.add(next_node)
                    path.append(next_node)
                    queue.append((next_node, depth + 1))
                    break  # 每次只选择一个后续节点以保持路径线性
        
        # 获取路径节点的详细信息
        node_map = {node["id"]: node for node in graph["nodes"]}
        path_nodes = [node_map[node_id] for node_id in path if node_id in node_map]
        
        return path_nodes
    
    def _find_optimal_starting_path(self, graph: Dict, max_depth: int) -> List[Dict]:
        """
        查找最优的起始学习路径（没有指定起始点时）
        
        Args:
            graph: 知识图谱
            max_depth: 最大深度
            
        Returns:
            路径节点列表
        """
        # 查找没有先修条件的知识点作为起始点
        start_candidates = [
            node for node in graph["nodes"] 
            if not node["prerequisites"]
        ]
        
        # 如果没有无先修条件的节点，选择先修条件最少的节点
        if not start_candidates:
            start_candidates = sorted(
                graph["nodes"], 
                key=lambda x: len(x["prerequisites"])
            )[:5]  # 取前5个先修条件最少的节点
        
        # 如果仍然没有候选节点，返回前几个节点
        if not start_candidates:
            return graph["nodes"][:min(max_depth, len(graph["nodes"]))]
        
        # 选择第一个候选节点作为起始点
        start_node = start_candidates[0]
        return self._find_path_from_start(graph, start_node["id"], max_depth)
    
    def get_recommended_resources(self, point_id: int) -> List[Dict]:
        """
        获取指定知识点的推荐学习资源
        
        Args:
            point_id: 知识点ID
            
        Returns:
            学习资源列表
        """
        db = self._get_db_session()
        try:
            resources = db.query(LearningResource).filter(
                LearningResource.point_id == point_id
            ).all()
            
            return [
                {
                    "id": resource.id,
                    "point_id": resource.point_id,
                    "type": resource.resource_type,
                    "title": resource.title,
                    "url": resource.url,
                    "description": resource.description,
                    "estimated_time": resource.estimated_time
                }
                for resource in resources
            ]
        finally:
            db.close()

# 全局知识图谱实例
knowledge_graph = KnowledgeGraph()